// 版权所有2020 etcd作者
// 
// 根据Apache许可证2.0版（以下简称“许可证”）获得许可；
// 除非遵守许可证，否则不得使用此文件。
// 您可以通过
// 
// www.apache.org/licenses/LICENSE-2.0 
// 
// 除非适用法律要求或书面同意，否则根据许可证分发的软件
// 按“原样”分发，
// 无任何明示或暗示的保证或条件。
// 请参阅许可证，了解管理许可的特定语言和
// 许可证下的限制。

package integration

import (
	"log"
	"net/http"
	"sync"
	"time"

	"go.etcd.io/etcd/client/pkg/v3/testutil"
	"go.etcd.io/etcd/client/pkg/v3/transport"
	"go.etcd.io/etcd/tests/v3/framework/integration"
)

// 为测试提供单个共享集群的基础设施-仅在需要时提供
// 基础设施。
// 参见/测试/集成/clientv3/examples/main\u测试。使用规范用法。
// 请注意，共享（LazyCluster）状态在
// 测试用例之间保留，因此剩余状态可能会产生交叉测试用例的影响。
// 对于实质性测试用例，更倾向于使用专用集群。

type LazyCluster interface {
	// EndpointsV2-公开客户端v2的连接点。
	// 调用此方法可能会初始化群集。
	EndpointsV2() []string

	// EndpointsV3-公开客户端v3的连接点。
	// 调用此方法可能会初始化群集。
	EndpointsV3() []string

	// 集群-调用此方法可能会初始化集群。
	Cluster() *integration.ClusterV3

	// 传输-调用此方法可能会初始化群集。
	Transport() *http.Transport

	Terminate()

	TB() testutil.TB
}

type lazyCluster struct {
	cfg       integration.ClusterConfig
	cluster   *integration.ClusterV3
	transport *http.Transport
	once      sync.Once
	tb        testutil.TB
	closer    func()
}

// NewLazyCluster返回在
// 第一次调用GetEndpoints（）或GetTransport（）时创建的新测试群集处理程序
func NewLazyCluster() LazyCluster {
	return NewLazyClusterWithConfig(integration.ClusterConfig{Size: 1})
}

// NewLazyClusterWithConfig返回第一次调用GetEndpoints（）或GetTransport（）时创建的新测试群集处理程序
// 
func NewLazyClusterWithConfig(cfg integration.ClusterConfig) LazyCluster {
	tb, closer := testutil.NewTestingTBProthesis("lazy_cluster")
	return &lazyCluster{cfg: cfg, tb: tb, closer: closer}
}

func (lc *lazyCluster) mustLazyInit() {
	lc.once.Do(func() {
		var err error
		lc.transport, err = transport.NewTransport(transport.TLSInfo{}, time.Second)
		if err != nil {
			log.Fatal(err)
		}
		lc.cluster = integration.NewClusterV3(lc.tb, &lc.cfg)
	})
}

func (lc *lazyCluster) Terminate() {
	lc.tb.Logf("Terminating...")
	if lc != nil && lc.cluster != nil {
		lc.cluster.Terminate(nil)
		lc.cluster = nil
	}
	if lc.closer != nil {
		lc.tb.Logf("Closer...")
		lc.closer()
	}
}

func (lc *lazyCluster) EndpointsV2() []string {
	return []string{lc.Cluster().Members[0].URL()}
}

func (lc *lazyCluster) EndpointsV3() []string {
	return lc.Cluster().Client(0).Endpoints()
}

func (lc *lazyCluster) Cluster() *integration.ClusterV3 {
	lc.mustLazyInit()
	return lc.cluster
}

func (lc *lazyCluster) Transport() *http.Transport {
	lc.mustLazyInit()
	return lc.transport
}

func (lc *lazyCluster) TB() testutil.TB {
	return lc.tb
}
